﻿var XLSX = require('xlsx');
var fs = require('fs');
var path = require('path');

var CSV = {
	DefaultOptions: {
		delim: ',',
		quote: '"',
		rowdelim: '\r\n',
        comment: '#'
	}
};

function CSVSyntaxError(msg) {
	this.message = msg;
	if (Error.captureStackTrace) {
		Error.captureStackTrace(this, arguments.callee);
	}
}
CSVSyntaxError.prototype = new Error();
CSVSyntaxError.prototype.constructor = CSVSyntaxError;
CSVSyntaxError.prototype.name = 'CSVSyntaxError';
if (new Error().toString() == '[object Error]') { // IE 6 7
	CSVSyntaxError.prototype.toString = function() {
		return this.name + ': ' + this.message;
	};
}

function CSVParser(str, options) {
	this.str = str;
	this.options = CSV.DefaultOptions;
	if (options) {
		options.delim = options.delim || CSV.DefaultOptions.delim;
		options.quote = options.quote || CSV.DefaultOptions.quote;
		if (options.quote.length != 1) {
			throw new RangeError('options.quote should be only 1 char');
		}
		options.rowdelim = options.rowdelim || CSV.DefaultOptions.rowdelim;
		this.options = options;
	}

	this.pos = 0;
	this.endpos = str.length;

	this.lineNo = 1;
}

CSVParser.prototype.next = function(s) {
	if (this.pos < this.endpos) {
		var len = s.length;
		if (this.str.substring(this.pos, this.pos + len) == s) {
			this.pos += len;
			return true;
		}
	}
	return false;
};

CSVParser.prototype.ahead = function(s) {
	if (this.pos < this.endpos) {
		if (!s) {
			return true;
		}
		var len = s.length;
		if (this.str.substring(this.pos, this.pos + len) == s) {
			return true;
		}
	}
	return false;
};

function countMatches(str, patt) {
	var count = 0;
	var i = str.indexOf(patt);
	while (i > 0) {
		count++;
		i = str.indexOf(patt, i + patt.length);
	}
	return count;
}

CSVParser.prototype.quotedField = function() {
	var mark = this.pos;
	if (!this.next(this.options.quote)) { this.pos = mark; return null; }
	var tmp = [];
	var start = this.pos;
	while (start < this.endpos) {
		var end = this.str.indexOf(this.options.quote, start);
		if (end < 0) {
			throw new CSVSyntaxError('line ' + this.lineNo + ': missing close quote');
		}
		var part = this.str.substring(start, end);
		this.lineNo += countMatches(part, '\n');
		tmp.push(part);
		if ((end + 1 < this.endpos) && (this.str.charAt(end + 1) == this.options.quote)) {
			start = end + 2;
			end = this.str.indexOf(this.options.quote, start);
		} else {
			this.pos = end + 1;
			break;
		}
	}
	return tmp.join(this.options.quote);
};


CSVParser.prototype.normalField = function() {
	var begin = this.pos;
	var idelim = this.str.indexOf(this.options.delim, begin);
	if (idelim < 0) {
		idelim = this.endpos;
	}
	var irowdelim = this.str.indexOf(this.options.rowdelim, begin);
	if (irowdelim < 0) {
		irowdelim = this.endpos;
	}
	this.pos = Math.min(idelim, irowdelim);
	return this.str.substring(begin, this.pos);
};


CSVParser.prototype.nextField = function() {
	var tmp = this.quotedField();
	if (tmp !== null) return tmp;
	return this.normalField();
};


CSVParser.prototype.nextRow_0 = function() {
	var mark = this.pos;
	if (!this.next(this.options.delim)) { this.pos = mark; return null; }
	var tmp = this.nextField();
	if (tmp === null) { this.pos = mark; return null; }
	return tmp;
};


CSVParser.prototype.nextRow = function() {
	var ar = [];
	var mark = this.pos;
	var tmp = this.nextField();
	if (tmp === null) { this.pos = mark; return null; }
	ar.push(tmp);
	tmp = this.nextRow_0();
	while (tmp !== null) {
		ar.push(tmp);
		tmp = this.nextRow_0();
	}
	if (!(this.next(this.options.rowdelim) || !this.ahead())) {
		throw new CSVSyntaxError('line ' + this.lineNo + ': ' + this.str.substring(Math.max(this.pos - 5, 0), this.pos + 5));
		this.pos = mark; return null;
	}
	if (this.str.charAt(this.pos - 1) == '\n') {
		this.lineNo++;
	}
	return ar;
};


CSVParser.prototype.hasNext = function() {
	return this.ahead();
};

CSV.CSVSyntaxError = CSVSyntaxError;
CSV.CSVParser = CSVParser;


CSV.parseOne = function(str, options) {
	var parser = new CSVParser(str, options);
	if (parser.hasNext()) {
		return parser.nextRow();
	}
	return null;
};


CSV.parse = function(str, options) {
	var parser = new CSVParser(str, options);
	var all = [];
	while (parser.hasNext()) {
		var ar = parser.nextRow();
		all.push(ar);
	}
	return all;
};


CSV.bindColumns = function(rows, colnames, fileName) {
    //找到以#开头的行的index
    var indexs = [];
    var length = rows.length;
    for (var j = 0; j < length; j++) {
        if (rows[j][0].indexOf(CSV.DefaultOptions.comment) === 0) {
            indexs.push(j);
        }
    }
    //从rows中移除以#开头的行
    length = indexs.length;
    for (var k = length-1; k >= 0; k--) {
        var index = indexs[k];
        rows.splice(index, 1);
    }

	if (!colnames) {
		colnames = rows.shift();
	}
	var rowObj = {};
	rows.map(function(row) {
		var obj = {};
		for (var i = 0; i < row.length; i++) {
			obj[colnames[i]] = row[i];
		}
		if (fileName == "language.txt") {
			rowObj[row[0]] = row[1];
		} else {
			rowObj[row[0]] = obj;
		}
		
		return obj;
	});

	return rowObj; 
};


if (!Array.prototype.map) {
	Array.prototype.map = function(callback, thisArg) {
		var T, A, k;
		if (this === null) {
			throw new TypeError(" this is null or not defined");
		}
		var O = Object(this);
		var len = O.length >>> 0;
		if ({}.toString.call(callback) != "[object Function]") {
			throw new TypeError(callback + " is not a function");
		}
		if (thisArg) {
			T = thisArg;
		}
		A = new Array(len);
		k = 0;
		while(k < len) {
			var kValue, mappedValue;
			if (k in O) {
				kValue = O[ k ];
				mappedValue = callback.call(T, kValue, k, O);
				A[ k ] = mappedValue;
			}
			k++;
		}
		return A;
	};
}

var xlsx2json = function(fromDir, toDir) {
	var files = fs.readdirSync(fromDir);
	files.forEach(function(fileName){
		if(path.extname(fileName) != '.xlsx') return;
		if(path.basename(fileName)[0] == '~') return;
		
		var fromPath = path.join(path.resolve(fromDir),fileName);
		var toPath = path.join(path.resolve(toDir),path.basename(fileName,'.xlsx'))+'.json';
		
		var workbook = XLSX.readFile(fromPath);
		var result = XLSX.utils.sheet_to_csv(workbook.Sheets[workbook.SheetNames[0]],{RS:'\r\n'});
		result = CSV.bindColumns(CSV.parse(result), null, fileName)
		
		fs.writeFileSync(toPath, JSON.stringify(result));
		console.log('完成',toPath);
	});
}
xlsx2json('xlsx','json');
